AWS Lambda(C#)でAPI Gatewayからパラメータを受け取る方法
1 はじめに
API GatewayからキックされるLambdaファンクションをC#で記述する場合に、パラメータを受け渡す方法について確認してみました。
作業には、Mac上で利用可能なAWS Lambda(C#)用テンプレートを使用しています。
Visual Studio for Mac で AWS Lambda(C#)を書くためのテンプレート
[GitHub] https://github.com/furuya02/AWSLambdaSample
上記のテンプレートをダウンロード(clone)して、Function.cs及びProgram.csを書き換えることで、検証を進めています。
2 LambdaSerializerAttribute
C#によるLambdaファンクションでは、下記のAssemby属性を指定することで、入力及び、戻り値のストリームで、.NETクラスとJSONの変換を行うことができます。(Assemby属性は、アセンブリレベル、若しくは、メソッドレベルで指定可能です。)
[assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
例えば、次ようなJSONデータは、下記のクラスに変換されます。
{"Name":"Taro","Age","25"}
public class Sample { public string Name { get; set; } public string Age { get; set; } }
API Gatewayでは、Integration Requestで、各種のデータをマッピングすることが出来るので、ここでリクエストパラメータをJSON形式で渡します。
3 特定のパラメータ名の取得
それでは、最初に、パラメータとして指定される名前が、予め決まっている場合の取得を試してみます。
パラメータとしてname及びageの2つの値を受け取るとした場合、まずは、マッピングで次のように指定します。
{ "Name": "$input.params('name')", "Age": "$input.params('age')" }
そして、C#によるLambdaファンクションでは、下記のとおりです。
using Amazon.Lambda.Core; [assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] namespace AWSLambdaSample { public class Function { public string FunctionHandler(Param param, ILambdaContext context) { var str = $"Name={param.Name} Age={param.Age}"; context.Logger.LogLine(str); return str; } } public class Param { public string Name { get; set; } public string Age { get; set; } } }
API Gatewayでテストボタンを押すと、パラメータに設定した値が、Lambdaで定義したクラスで受け取れていることを確認できます。
CloudWatch Logsでは、ファンクション内で出力したログも確認できます。
デプロイして実際にアクセスしている様子です。
4 すべてのパラメータの取得
先の方法では、予め決まったパラメータ以外は、取得できません。そこで、すべてのパラメータをStringとして受けとってみます。
マッピングは、次のように指定します。
{ "Params": "$input.params().querystring" }
.NETのクラス定義です。
public string FunctionHandler(Param param, ILambdaContext context) { var str = $"Params={param.Params}"; context.Logger.LogLine(str); return str; } public class Param { public string Params { get; set; } }
5 すべてのパラメータをDictionaryで取得
最後に、もう一歩進めて、すべてのパラメータをDictionaryで受け取る要領です。
マッピング
{ "Params": { #foreach($param in $input.params().querystring.keySet()) "$param": "$util.escapeJavaScript($input.params().querystring.get($param))" #if($foreach.hasNext),#end #end } }
.NETのクラス定義
public string FunctionHandler(Param param, ILambdaContext context) { var result = new StringBuilder(); foreach (var d in param.Params) { var str = $"Key={d.Key} Value={d.Value}"; context.Logger.LogLine(str); result.Append(str); } return result.ToString(); } public class Param { public Dictionary<string,string> Params { get; set; } }
6 String以外の型変換
実は、String以外の型でも変換は可能です。 次の例は、Int型及びDateTime型に変換して受け渡しを行っている例です。
マッピング
{ "Age": "$input.params('age')", "Birthday": "$input.params('birthday')" }
.NETのクラス定義
public string FunctionHandler(Param param, ILambdaContext context) { var str = $"Age={param.Age} Birthday={param.Birthday.ToString("yyyy.MM.dd")}"; context.Logger.LogLine(str); return str; } public class Param { public int Age { get; set; } public DateTime Birthday { get; set; } }
しかし、ここで注意が必要なのは、無効な値がセットされた場合に、Amazon.Lambda.Serialization.Json.JsonSerializer自体が例外で落ちてしまいますので、ファンクション内でのエラー制御はできません。
ガチガチの入力以外では、とりあえずStringで受けておいて、内部で変換する方が安全かも知れません。
7 最後に
ここまで見てきたように、API Gateway側のマッピングで必要なデータをJSON形式にまとめれば、適切なクラスを設計することで、自由にデータの受け渡しが可能です。
今回は、クエリーパラメータのみをマッピングしてみましたが、この他にも、リクエストヘッダ($input.params().header)、リクエストボディ($input.body)、接続元アドレス($context.identity.sourceIp)、ユーザーエージェント($context.identity.userAgent)など各種のデータをマッピング可能です。
詳しくは、下記をご残照ください。
API Gateway API リクエストとレスポンスペイロードのマッピングテンプレートのリファレンス
8 参考資料
How to pass a querystring or route parameter to AWS Lambda from Amazon API Gateway